winbrew_app\operations\remove/mod.rs
1//! End-to-end package removal workflow for `winbrew remove`.
2//!
3//! Removal is split into two phases so the CLI can reason about impact before
4//! it mutates anything:
5//!
6//! - [`plan_removal`] loads the target package and collects dependent packages.
7//! - [`execute_removal`] applies the plan and performs engine-specific cleanup.
8//!
9//! The high-level [`remove`] helper simply composes those phases for callers
10//! that want a one-shot operation. The CLI typically uses the lower-level plan
11//! and execution functions separately so it can show a warning, ask for
12//! confirmation, and then decide whether to proceed.
13//!
14//! Dependency checks are conservative by default. If a package is still
15//! required by another installed package, removal is blocked unless the caller
16//! explicitly opts into `force`. The plan itself is still built so the caller
17//! can see exactly which dependents were discovered.
18
19mod execution;
20mod plan;
21
22use thiserror::Error;
23
24use crate::models::domains::install::InstallerType;
25
26pub use crate::models::domains::install::RemovalPlan;
27pub use execution::execute_removal;
28pub use plan::{find_dependents, plan_removal};
29
30/// Errors produced by the removal workflow.
31///
32/// The variants separate policy failures from engine support gaps and from any
33/// lower-level runtime error that escapes the removal engine or filesystem
34/// cleanup path.
35#[derive(Debug, Error)]
36pub enum RemovalError {
37 /// The package cannot be removed because another installed package still depends on it.
38 #[error("cannot remove '{name}' because it is required by: {dependents}")]
39 DependentPackagesBlocked { name: String, dependents: String },
40
41 /// The installed package type does not have a supported removal strategy.
42 #[error("unsupported package type: {kind}")]
43 UnsupportedPackageType { kind: InstallerType },
44
45 /// A lower-level error escaped the removal pipeline.
46 #[error(transparent)]
47 Unexpected(#[from] anyhow::Error),
48}
49
50/// Convenience result type for removal operations.
51pub type Result<T> = std::result::Result<T, RemovalError>;
52
53/// Plan and execute package removal in one call.
54///
55/// This helper is intentionally small: it first resolves a removal plan, then
56/// hands the plan to the execution layer. It does not perform UI prompting or
57/// confirmation logic, so callers that need to warn the user about dependent
58/// packages should use [`plan_removal`] directly before deciding whether to
59/// call this function.
60pub fn remove(name: &str, force: bool) -> Result<()> {
61 let plan = plan_removal(name)?;
62
63 execute_removal(&plan, force)
64}